#include "factory.h"
//线程池初始化
int factory_init(Factory_t *pf,int thread_num,int capacity)
{
    que_init(&pf->que,capacity);//队列初始化
    int ret=pthread_cond_init(&pf->cond,NULL);//条件变量初始化
    THREAD_ERR_CHECK(ret,"pthread_cond_init");
    pf->pthid=(pthread_t *)calloc(thread_num,sizeof(pthread_t));//为线程id申请空间
    pf->start_flag=0;//线程池启动标志设为0
    return 0;
}
//线程池启动
int factory_start(Factory_t *pf,int thread_num)
{
    if(!pf->start_flag)//如果线程池没启动
    {
        int i;
        for(i=0;i<thread_num;i++)
        {
            pthread_create(pf->pthid+1,NULL,thread_handle,pf);
        }
        pf->start_flag=1;
    }
}

//释放锁资源
void cleanup(void* p)
{
    pthread_mutex_unlock((pthread_mutex_t*)p);
}
//子线程流程
void* thread_handle(void* p)
{
    Factory_t* pf=(Factory_t*)p;
    Client_State_t *pnew=(Client_State_t *)p;
    pQue_t pq=&(pf->que);//让一个队列指针指向队列
    pNode_t pcur=NULL;//取队列结点
    //建立puts/gets专属传输路线
    int tran_fd;
    while(1)
    {
        pthread_mutex_lock(&pq->mutex);
        pthread_cleanup_push(cleanup,&pq->mutex);
        if(0==pq->que_size)//如果队列为空
        {
            pthread_cond_wait(&pf->cond,&pq->mutex);
        }
        que_get(pq,&pcur);////取队列结点
        pthread_cleanup_pop(1);//直接通过pop来解锁
        if(pcur)//pcur不为NULL代表拿到了结点
        {
            int new_socketfd = socket(AF_INET,SOCK_STREAM,0);
            int ret;
            struct sockaddr_in my_addr2;
            int reuse=1;
            ret = setsockopt(new_socketfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse));
            memset(&my_addr2,0,sizeof(struct sockaddr_in));
            my_addr2.sin_family = AF_INET;
            my_addr2.sin_port = htons(atoi(pnew->port));
            my_addr2.sin_addr.s_addr=inet_addr(pnew->ip);
            ret=bind(new_socketfd,(struct sockaddr*)&my_addr2,sizeof(my_addr2));
            //ERROR_CHECK(ret,-1,"bind");
            ret = listen(new_socketfd,10);
            //ERROR_CHECK(ret,-1,"listen");
            tran_fd=accept(new_socketfd,NULL,NULL);
            printf("start transfer!%d\n",pcur->rv_cmd->cmd_type);
            pcur->tran_fd =tran_fd;
            ret=server_token(tran_fd);
            if(ret==-1)//验证失败
            {
                printf("token error!\n");
            }
            else{
                ret =tran_file(pcur->tran_fd,pcur->rv_cmd);
                if(ret==-1)
                {
                    printf("something leave!\n"); 
                }
                else{
                    printf("Trans Success!\n");
                }
                free(pcur);
                pcur=NULL;
                close(tran_fd);
                close(new_socketfd);
                continue;
            }
        }
    }
}

//客户端输入用户名和密码进行登录
int server_login(int new_fd,train_state_t* ct,usraddr usr,char* salt,pcque pclient_que)
{
    int data_len,ret;
    char buf[1000]={0};
    int s_ret=check_usr(usr.usrA,salt);
    //用户名匹配成功就把salt传给客户端，如果匹配失败了，那就把这辆火车数据部分的长度设为0
    if(0==s_ret)//用户名匹配成功
    {
        //发送salt
        ct->data_len=strlen(salt);//将salt长度装到火车头
        ret=send_n(new_fd,&ct->data_len,sizeof(int));
        SELFFUNC_ERR_CHECK(ret,"send_n");
        ret=send_n(new_fd,&salt,4+ct->data_len);
        SELFFUNC_ERR_CHECK(ret,"send_n");
        //接收客户端发送的加密字符串
        ret=recv_n(new_fd,&data_len,sizeof(int));//接收加密字符串长度
        SELFFUNC_ERR_CHECK(ret,"recv_n");
        ret=recv_n(new_fd,&buf,data_len);//接收加密字符串内容
        SELFFUNC_ERR_CHECK(ret,"recv_n");
        strcpy(usr.pwd,buf);
        //对比本地存储的加密密码
        s_ret=check_pwd(usr.usrA,usr.pwd);
        //用户名和对应密码均匹配,将salt作为token发送给客户端，否则发送空货车表示密码错误
        if(0==s_ret)
        {
            ct->data_len=sizeof(salt);
            ret=send_n(new_fd,&ct->data_len,sizeof(int));
            SELFFUNC_ERR_CHECK(ret,"send_n");
            ret=send_n(new_fd,&salt,4+ct->data_len);
            SELFFUNC_ERR_CHECK(ret,"send_n");
            //加入一个client,头插
            enter_new_client(pclient_que,new_fd,usr.usrA,salt);
            pclient_que->que_size++;
            printf("client enter,%d left\n\n",pclient_que->que_size);
            show_client(pclient_que->que_head,pclient_que->que_tail);
            userlogin_note(usr.usrA);
        }
        else{
            ct->data_len=0;
            ret=send_n(new_fd,&data_len,sizeof(int));
            return -1;
        }
    }
    else{
        ct->data_len=0;
        ret=send_n(new_fd,&data_len,sizeof(int));
        printf("%s failed to login!\n",usr.usrA);
        return -1;
    }
}

//客户端输入用户名和密码进行注册
int server_register(int new_fd,train_state_t* ct,usraddr usr,char* salt,pcque pclient_que)
{
    int ret;
    char *crpwd;
    //接用户名
    ret=recv_n(new_fd,&usr.data_len,sizeof(int));//接用户名长度 
    SELFFUNC_ERR_CHECK(ret,"recv_n");
    ret=recv_n(new_fd,&usr.usrA,usr.data_len);//接用户名内容
    SELFFUNC_ERR_CHECK(ret,"recv_n");
    //接密码
    ret=recv_n(new_fd,&usr.data_len,sizeof(int));
    SELFFUNC_ERR_CHECK(ret,"recv_n");
    ret=recv_n(new_fd,&usr.pwd,usr.data_len);
    SELFFUNC_ERR_CHECK(ret,"recv_n");
    //计算salt
    Make_salt(salt);
    //计算加密密码
    crpwd=crypt(usr.pwd,salt);//将用户的密码与salt加密得到一个与密码文件中密码进行对比的加密字符串
    sprintf(usr.pwd,"%s",crpwd);
    //检查有无重复用户名
    ret=check_duplicates(usr.usrA);
    if(-1==ret)//有重复的用户，注册失败
    {
        printf("%s exited\n",usr.usrA);
        //发一节空火车告诉客户端注册失败
        ct->data_len=0;
        ret=send_n(new_fd,&ct->data_len,sizeof(int));
        return -1;
    }
    else{
        ret=new_user_insert(usr.usrA,salt,usr.pwd);//向数据库中插入新用户的信息
        if(ret==0)//插入成功
        {
            printf("new user %s regist success\n",usr.usrA);
        }
        ct->data_len=sizeof(salt);
        ret=send_n(new_fd,&ct->data_len,sizeof(int));
        SELFFUNC_ERR_CHECK(ret,"send_n");
        ret=send_n(new_fd,&salt,4+ct->data_len);
        SELFFUNC_ERR_CHECK(ret,"send_n");
        //加入一个client,头插
        enter_new_client(pclient_que,new_fd,usr.usrA,salt);
        pclient_que->que_size++;
        printf("client enter,%d left\n\n",pclient_que->que_size);
        userlogin_note(usr.usrA);
    }
}

//把path修改为上一级
void goto_prepath(char *path)
{
    int i=0;
    int num=0;
    int num1=0;
    char* p=path;
    //数有几个'/'
    while((*p!=' ')&&(*p!='\0'))
    {
        if(*p=='/')
        {
            num++;
        }
        p=p+1;
    }
    p=path;
    while(1)
    {
        if(*p=='/')
        {
            num1++;
        }
        if(num1==num)
        {
            break;
        }
    }
    *p!='\0';
}

//进行一次遍历查找，创建对应的client加入队列
void enter_new_client(pcque pclientque,int new_fd,char* username,char* salt)
{
    pcnode pnew=(pcnode)malloc(sizeof(cnode));

    //修改该用户的结点
    pnew->client.new_fd=new_fd;
    pnew->client.code=0;
    char path_buf[100];
    sprintf(path_buf,"%s/",username);
    strcpy(pnew->client.path,path_buf);
    strcpy(pnew->client.token,salt);
    strcpy(pnew->client.c_name,username);
    
    clientque_insert(pclientque,pnew);
    printf("user %s's new_fd %d has entered client_quec.\n",username,new_fd);
}

//遍历clientque并打印
void  show_client(pcnode head,pcnode tail)
{
    if(head==tail)
    {
        printf("only username = %s\n",head->client.c_name);
    }
    else
    {
        pcnode p=head;
        while(p!=tail)
        {
            printf("username = %s\n",p->client.c_name);
            p=p->next_client;
        }
    }
}